package com.zhj.psi.core.mvc;

import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.google.common.collect.Lists;
import org.apache.commons.io.IOUtils;
import org.springframework.core.GenericCollectionTypeResolver;
import org.springframework.core.MethodParameter;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.support.WebDataBinderFactory;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.ModelAndViewContainer;

import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.math.BigDecimal;
import java.util.List;
import java.util.Map;

@Component
public class JsonPathArgumentResolver implements HandlerMethodArgumentResolver {

	private static final String JSONBODYATTRIBUTE = "JSON_REQUEST_BODY";

	private ObjectMapper objectMapper;

	@Override
	public boolean supportsParameter(MethodParameter parameter) {
		return parameter.hasParameterAnnotation(JsonArg.class);
	}

	@SuppressWarnings("unchecked")
	@Override
	public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
		String body = getRequestBody(webRequest);

		// 注册自定义的反序列化规则
		SimpleModule simpleModule = new SimpleModule();
	    simpleModule.addDeserializer(Object.class, new CustomDateDeseralizer());
	    objectMapper.registerModule(simpleModule);

		Map<String, Object> params = objectMapper.readValue(body, new TypeReference<Map<String, Object>>() {});
		Object param = params.get(parameter.getParameterAnnotation(JsonArg.class).value());
		if (param instanceof Map) {
			if (JsonMapWrapper.class.isAssignableFrom(parameter.getParameterType())) {
				return new JsonMapWrapper((Map<String, Object>)param);
			}
			else {
				param = objectMapper.readValue(objectMapper.writeValueAsString(param), parameter.getParameterType());
			}
		}
		else if (param instanceof List) {
			Class genericClass = Class.class.cast(GenericCollectionTypeResolver.getCollectionParameterType(parameter));
			List<Object> result = Lists.newArrayList();
			for (Object instance : (List)param) {
				result.add(objectMapper.readValue(objectMapper.writeValueAsString(instance), genericClass));
			}
			return result;
		}
		else if (parameter.getParameterType().toString().equals("class java.math.BigDecimal")) {
			if (param instanceof Integer) {
				param = BigDecimal.valueOf((int) param);
			} else if (param instanceof Double)
                param = BigDecimal.valueOf((double)param);
			else if (param instanceof String) {
				param = new BigDecimal((String)param);
			}
		}
		return param;
	}

	private String getRequestBody(NativeWebRequest webRequest) {
		HttpServletRequest servletRequest = webRequest.getNativeRequest(HttpServletRequest.class);
		String jsonBody = (String) servletRequest.getAttribute(JSONBODYATTRIBUTE);
		if (jsonBody == null) {
			try {
				String body = IOUtils.toString(servletRequest.getInputStream());
				servletRequest.setAttribute(JSONBODYATTRIBUTE, body);
				return body;
			} catch (IOException e) {
				throw new RuntimeException(e);
			}
		}
		return jsonBody;

	}

	public ObjectMapper getObjectMapper() {
		return objectMapper;
	}

	public void setObjectMapper(ObjectMapper objectMapper) {
		this.objectMapper = objectMapper;
	}
}